# -*- coding: utf-8 -*-
"""
Database connection and related functions
"""

#---------------------------------------------------------------------------
# Copyright (c) 2014 Ahmad Ghulam Zakiy <ghulam.zakiy@gmail.com>
# License: GPL3
#
# This file is part of SimpleStore
#
# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#---------------------------------------------------------------------------

from PySide import QtSql, QtCore
from lib import logger
import os
import config


class database(QtCore.QObject):
    """
       Kelas koneksi ke database
    """

    connectionName = QtSql.QSqlDatabase.defaultConnection
    connectionDriver = config.database['driver']
    connectionHost = config.database['hostname']
    connectionDB   = config.database['database']
    connectionUsername = config.database['username']
    connectionPassword = config.database['password']
    connectionOptions  = config.database['options']

    db    = None
    __schema = {}
    __tables = []
    __open   = False
    __tablesReady = False
    __tableFieldsReady = False
    __transactionsSupported = False
    __transactionStarted = False
    __transactionFinished = 0
    __transactionCommitted = 1
    __transactionRolledback = 2
    query = None
    log = None
    transactionsStarted = QtCore.Signal(bool)
    transactionsFinished = QtCore.Signal(int)
    

    def alterSetup(self, driver='', host='', db='', username='', password='', option=''):
        """
        Digunakan bila ingin terkoneksi dengan database selain database default.
        Menerima parameter driver, host, db, username, password dan options.
        Dipanggil sebelum initDB.
        """
        self.connectionDriver = driver if driver > '' else self.connectionDriver        
        self.connectionHost = host if host > '' else self.connectionHost
        self.connectionDB = db if db > '' else self.connectionDB
        self.connectionUsername = username if username > '' else self.connectionUsername
        self.connectionPassword = password if password > '' else self.connectionPassword
        self.connectionOptions = options if options > '' else self.connectionOptions


    def __prep(self):

        if isinstance(self.db, QtSql.QSqlDatabase) == False:
           self.db = QtSql.QSqlDatabase.addDatabase(self.connectionDriver)
           self.log = logger.log()
           
        self.db.setConnectOptions(self.connectionOptions)
        self.db.setHostName(self.connectionHost)
        self.db.setDatabaseName(self.connectionDB)
        self.db.setUserName(self.connectionUsername)
        self.db.setPassword(self.connectionPassword)
        self.log.logMe(title="Database", text="Preparing '%s'..." % self.connectionDB)
           

    def initDB(self):
        """
            Memulai koneksi ke database, akan memuat koneksi yang ada bila telah tersedia
        """

        if isinstance(self.db, QtSql.QSqlDatabase) == True:

           if self.db.isOpen() == True:
              self.log.logMe(title="Connection is OK", text="You don't need to reinitialize this")
              return True
           else:
              self.__prep()
              if self.db.open():
                 self.__open = True;
                 self.connectionName = self.db.connectionName()
                 self.log.logMe(title="Connection", text="Re-opened")
                 if self.db.driver().hasFeature(QtSql.QSqlDriver.Transactions):
                      self.__transactionsSupported = True
                      self.log.logMe(title="Transactions", text="Supported")
                 else:
                      self.log.logMe(title="Transactions", text="Not supported")
                 return True
              else:
                 self.log.logMe(title="Connection", text="Re-open error")
                 self.log.logMe(title="Dump", text=self.db.lastError().text())
                 return False

        else:
           self.__prep()
           if self.db.open():
              self.__open = True;
              self.connectionName = self.db.connectionName()
              self.log.logMe(title="Connection", text="Opened")
              if self.db.driver().hasFeature(QtSql.QSqlDriver.Transactions):
                  self.__transactionsSupported = True
                  self.log.logMe(title="Transactions", text="Supported")
              else:
                  self.log.logMe(title="Transactions", text="Not supported")
              return True
           else:
              self.log.logMe(title="Connection", text="Error")
              self.log.logMe(title="Dump", text=self.db.lastError().text())
              return False


    def isOpen(self):
        if self.__open:
            return self.__open
        else:
            self.log.logMe(title="Connection", text="Not opened")
            return False


    def isTransactionsSupported(self):
        if self.isOpen() & (self.__transactionsSupported == True):
            return self.__transactionsSupported
        else:
            self.log.logMe(title="Transactions", text="Not supported")
            return False


    def startTransactions(self):
        if self.isOpen() & self.isTransactionsSupported():
            if self.db.transaction() == True:
                self.__transactionStarted = True
                self.transactionsStarted.emit(True)
                self.log.logMe(title="Transactions", text="Started")
                return True
            else:
                self.transactionsStarted.emit(False)
                self.log.logMe(title="Transactions", text="Not started")
                return False


    def commitTransactions(self):
        if self.__transactionStarted:
            if self.db.commit() == True:
                self.__transactionFinished = database.__transactionCommitted
                self.transactionsFinished.emit(self.__transactionFinished)
                self.log.logMe(title="Transactions", text="Committed")
                return True
            else:
                self.log.logMe(title="Transactions", text="Not committed")
                return False


    def rollbackTransactions(self):
        if self.__transactionStarted:
            if self.db.rollback() == True:
                self.__transactionFinished = database.__transactionRolledback
                self.transactionsFinished.emit(self.__transactionFinished)
                self.log.logMe(title="Transactions", text="Rolledback")
                return True
            else:
                self.log.logMe(title="Transactions", text="Not rolledback")
                return False


    def isTransactionsFinished(self):
        return self.__transactionFinished

    
    def initTables(self):
        if self.isOpen() & (self.__tablesReady == False):           
            self.__tables = self.db.tables(QtSql.QSql.AllTables)
            self.__tablesReady = True
            self.log.logMe(title="Tables", text="Ready")
            return True


    def isTablesReady(self):
        if self.isOpen() & self.__tablesReady:
            return True
        else:
            self.log.logMe(title="Tables", text="Not ready")


    def getTables(self):
        if self.isTablesReady():
            return self.__tables


    def isValidTable(self, tableName):
        if self.isTablesReady():
           if self.__tables.__contains__(tableName):
              return True
           else:
              self.log.logMe(title="Table name", text="%s is invalid" % tableName)
              return False


    def initTablesFields(self):
        if self.isTablesReady():
           for tableName in self.__tables:
               self.Query('DESCRIBE %s' % tableName)
               newList = []
               while self.query.next():
                     newList.append(self.query.value(0))
               self.__schema.__setitem__(tableName, newList)
               self.query.clear()
           self.__tableFieldsReady = True
           self.log.logMe(title="Tables fields", text="Ready")
           return True


    def getTableFields(self, tableName):
        if self.__tableFieldsReady:
           if self.isValidTable(tableName):
              return self.__schema[tableName]
           

    def isValidTableField(self, tableName, fieldName):
        if self.__tableFieldsReady:
           if self.isValidTable(tableName):
              if self.__schema[tableName].__contains__(fieldName):
                  return True
              else:
                  self.log.logMe(title="Table field", text="%s.%s is invalid" % (tableName, fieldName))
                  return False


    def Query(self, string):
        """
        NOTE: Jangan gunakan self.query sebagai iterasi data dalam implementasi classmu, buat lah 
        instance baru dari QtSql.QSqlQuery karena itu akan memudahkan garbage collector menghapusnya.
        """
        self.query = QtSql.QSqlQuery(string, self.db)
        return self.query
        
        
    def lastQuery(self):
        if isinstance(self.query, QtSql.QSqlQuery):
           if QtSql.QSqlQuery.last(self.query):
              return unicode(QtSql.QSqlQuery.lastQuery(self.query))
            
        
    def closeDB(self):
        if isinstance(self.query, QtSql.QSqlQuery):
           if self.query.isActive() == True:            
              self.query.clear()
           if self.db.isOpen():
              self.db.close()
              self.__open = False
              self.log.logMe(title="Connection", text="Closed")
        
    
    def __del__(self):
        self.closeDB()
        self.db.removeDatabase(self.connectionName)
        self.log.logMe(title="Database", text="Unprep '%s'..." % self.connectionDB)
    

